---
title: "指標基礎"
description: "從JavaScript開發者的角度理解C語言指標。學習指標基礎、操作、空指標和安全的指標程式設計實踐。"
---

# 指標基礎

## 1. 介紹

### 從JavaScript參照到C指標

作為JavaScript開發者，你熟悉參照 - 當你將物件賦值給變數時，你正在建立對該物件的參照。在C語言中，指標是這個概念的顯式、低階版本，讓你直接控制記憶體位址。

指標是C語言最強大和最具挑戰性的特性之一。它們允許你：
- 直接操作記憶體位址
- 在函數間高效傳遞資料
- 建立動態資料結構
- 最佳化效能關鍵程式碼

> 💡 **關鍵概念**: 指標是儲存另一個變數記憶體位址的變數。將其視為「郵寄位址」，告訴你實際資料在哪裡。

## 2. 指標基礎

### 2.1 什麼是指標？

<UniversalEditor title="指標基礎" compare={true}>
```javascript !! js
// JavaScript - 隱式參照
let x = 42;
let ref = x;  // ref包含值42，不是參照

let obj = { name: "John", age: 30 };
let objRef = obj;  // objRef參照同一個物件

// 透過參照修改
objRef.age = 31;
console.log(obj.age);  // 31 (同一個物件)

// JavaScript不暴露記憶體位址
// (除了除錯工具)
console.log(x, ref, obj, objRef);
```

```c !! c
#include <stdio.h>

int main() {
    int x = 42;
    int* ptr = &x;  // ptr儲存x的位址
    
    printf("x的值: %d\n", x);
    printf("x的位址: %p\n", &x);
    printf("ptr的值: %p\n", ptr);
    printf("ptr指向的值: %d\n", *ptr);
    
    // 透過指標修改
    *ptr = 100;
    printf("修改後 - x: %d\n", x);
    
    return 0;
}
```
</UniversalEditor>

### 2.2 指標宣告和初始化

<UniversalEditor title="指標宣告" compare={true}>
```javascript !! js
// JavaScript - 沒有顯式指標概念
let value = 42;
let reference = value;  // 值複製，不是指標

// 物件透過參照傳遞
let person = { name: "Alice", age: 25 };
let personRef = person;  // 對同一物件的參照

console.log("值:", value);
console.log("參照:", reference);
console.log("人員:", person);
console.log("人員參照:", personRef);
```

```c !! c
#include <stdio.h>

int main() {
    // 指標宣告
    int* ptr1;        // 宣告指向int的指標
    int *ptr2;        // 替代語法
    int * ptr3;       // 另一種替代
    
    // 指標初始化
    int x = 42;
    int* ptr = &x;    // 用x的位址初始化
    
    // 多個指標宣告
    int a = 10, b = 20;
    int *ptr_a = &a, *ptr_b = &b;
    
    printf("x = %d, 位址 = %p\n", x, &x);
    printf("ptr = %p, *ptr = %d\n", ptr, *ptr);
    printf("a = %d, ptr_a = %p, *ptr_a = %d\n", a, ptr_a, *ptr_a);
    printf("b = %d, ptr_b = %p, *ptr_b = %d\n", b, ptr_b, *ptr_b);
    
    return 0;
}
```
</UniversalEditor>

### 2.3 取位址和解參照操作符

<UniversalEditor title="位址和解參照操作符" compare={true}>
```javascript !! js
// JavaScript - 沒有直接記憶體位址存取
let value = 42;
let copy = value;

// JavaScript沒有:
// - 取位址操作符 (&)
// - 解參照操作符 (*)
// - 直接記憶體操作

console.log("值:", value);
console.log("複製:", copy);

// 物件工作方式不同
let obj = { data: 100 };
let objCopy = obj;  // 參照複製
objCopy.data = 200;
console.log("原始物件:", obj.data);  // 200 (同一個物件)
```

```c !! c
#include <stdio.h>

int main() {
    int value = 42;
    
    // 取位址操作符 (&)
    int* ptr = &value;  // 獲取value的位址
    
    // 解參照操作符 (*)
    int dereferenced = *ptr;  // 獲取位址處的值
    
    printf("原始值: %d\n", value);
    printf("值的位址: %p\n", &value);
    printf("指標值: %p\n", ptr);
    printf("解參照的值: %d\n", dereferenced);
    
    // 透過指標修改
    *ptr = 100;
    printf("修改後: value = %d\n", value);
    
    // 多級間接參照
    int** ptr_to_ptr = &ptr;
    printf("指向指標的指標: %p\n", ptr_to_ptr);
    printf("透過雙重解參照的值: %d\n", **ptr_to_ptr);
    
    return 0;
}
```
</UniversalEditor>

## 3. 指標類型和類型安全

### 3.1 類型化指標

<UniversalEditor title="類型化指標" compare={true}>
```javascript !! js
// JavaScript - 動態類型
let value = 42;
let string = "hello";
let array = [1, 2, 3];

// JavaScript沒有類型化參照
// 所有變數都可以參照任何類型
let ref1 = value;
let ref2 = string;
let ref3 = array;

console.log(ref1, typeof ref1);
console.log(ref2, typeof ref2);
console.log(ref3, typeof ref3);
```

```c !! c
#include <stdio.h>

int main() {
    // 不同類型需要不同的指標類型
    int int_value = 42;
    char char_value = 'A';
    float float_value = 3.14f;
    double double_value = 2.718;
    
    // 類型化指標
    int* int_ptr = &int_value;
    char* char_ptr = &char_value;
    float* float_ptr = &float_value;
    double* double_ptr = &double_value;
    
    printf("int: 值=%d, 指標=%p, 解參照=%d\n", 
           int_value, int_ptr, *int_ptr);
    printf("char: 值=%c, 指標=%p, 解參照=%c\n", 
           char_value, char_ptr, *char_ptr);
    printf("float: 值=%f, 指標=%p, 解參照=%f\n", 
           float_value, float_ptr, *float_ptr);
    printf("double: 值=%lf, 指標=%p, 解參照=%lf\n", 
           double_value, double_ptr, *double_ptr);
    
    // 類型安全 - 錯誤的指標類型會導致問題
    // int* wrong_ptr = &char_value;  // 警告: 不相容的指標類型
    
    return 0;
}
```
</UniversalEditor>

### 3.2 空指標

<UniversalEditor title="空指標" compare={true}>
```javascript !! js
// JavaScript - 所有變數都可以持有任何類型
let dynamicVar = 42;
dynamicVar = "hello";
dynamicVar = [1, 2, 3];
dynamicVar = { key: "value" };

// JavaScript不需要空指標
// 因為所有變數本質上都是靈活的
console.log(dynamicVar, typeof dynamicVar);
```

```c !! c
#include <stdio.h>

int main() {
    // 空指標 - 可以指向任何類型
    void* void_ptr;
    
    int int_value = 42;
    char char_value = 'A';
    float float_value = 3.14f;
    
    // 指向不同類型
    void_ptr = &int_value;
    printf("指向int的空指標: %p\n", void_ptr);
    
    void_ptr = &char_value;
    printf("指向char的空指標: %p\n", void_ptr);
    
    void_ptr = &float_value;
    printf("指向float的空指標: %p\n", void_ptr);
    
    // 要使用空指標，必須轉換回適當類型
    void_ptr = &int_value;
    int* int_ptr = (int*)void_ptr;
    printf("轉換回int: %d\n", *int_ptr);
    
    return 0;
}
```
</UniversalEditor>

## 4. 指標算術

### 4.1 基本指標算術

<UniversalEditor title="指標算術" compare={true}>
```javascript !! js
// JavaScript - 沒有指標算術
let arr = [1, 2, 3, 4, 5];

// 透過索引存取陣列
console.log(arr[0]);  // 1
console.log(arr[1]);  // 2
console.log(arr[2]);  // 3

// JavaScript沒有:
// - 指標算術
// - 記憶體位址操作
// - 直接記憶體遍歷

// 陣列迭代
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}
```

```c !! c
#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int* ptr = arr;  // 指向第一個元素
    
    printf("使用指標算術的陣列元素:\n");
    
    // 指標算術
    printf("ptr[0] = %d\n", *ptr);        // 第一個元素
    printf("ptr[1] = %d\n", *(ptr + 1));  // 第二個元素
    printf("ptr[2] = %d\n", *(ptr + 2));  // 第三個元素
    
    // 陣列表示法 vs 指標算術
    printf("\n陣列表示法 vs 指標算術:\n");
    printf("arr[0] = %d, *ptr = %d\n", arr[0], *ptr);
    printf("arr[1] = %d, *(ptr+1) = %d\n", arr[1], *(ptr + 1));
    printf("arr[2] = %d, *(ptr+2) = %d\n", arr[2], *(ptr + 2));
    
    // 不同類型的指標算術
    char char_arr[] = {'A', 'B', 'C', 'D'};
    char* char_ptr = char_arr;
    
    printf("\n使用指標算術的字元陣列:\n");
    printf("char_ptr[0] = %c\n", *char_ptr);
    printf("char_ptr[1] = %c\n", *(char_ptr + 1));
    printf("char_ptr[2] = %c\n", *(char_ptr + 2));
    
    return 0;
}
```
</UniversalEditor>

### 4.2 指標遞增和遞減

<UniversalEditor title="指標遞增/遞減" compare={true}>
```javascript !! js
// JavaScript - 沒有指標遞增/遞減
let arr = [10, 20, 30, 40, 50];

// 使用索引的陣列迭代
for (let i = 0; i < arr.length; i++) {
    console.log(`arr[${i}] = ${arr[i]}`);
}

// 使用for...of的陣列迭代
for (let value of arr) {
    console.log(`值: ${value}`);
}

// JavaScript沒有指標遍歷
```

```c !! c
#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int* ptr = arr;
    
    printf("使用指標遞增的陣列遍歷:\n");
    
    // 使用指標遞增
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d (位址: %p)\n", i, *ptr, ptr);
        ptr++;  // 移動到下一個元素
    }
    
    printf("\n使用指標遞減的陣列遍歷:\n");
    
    // 重置指標到末尾並遞減
    ptr = arr + 4;  // 指向最後一個元素
    for (int i = 4; i >= 0; i--) {
        printf("arr[%d] = %d (位址: %p)\n", i, *ptr, ptr);
        ptr--;  // 移動到前一個元素
    }
    
    // 不同步長的指標算術
    printf("\n使用不同遞增步長遍歷陣列:\n");
    ptr = arr;
    printf("每個元素: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", *ptr);
        ptr += 1;  // 與ptr++相同
    }
    printf("\n");
    
    ptr = arr;
    printf("每隔一個元素: ");
    for (int i = 0; i < 5; i += 2) {
        printf("%d ", *ptr);
        ptr += 2;  // 跳過一個元素
    }
    printf("\n");
    
    return 0;
}
```
</UniversalEditor>

## 5. 空指標和指標安全

### 5.1 空指標

<UniversalEditor title="空指標" compare={true}>
```javascript !! js
// JavaScript - null和undefined
let nullValue = null;
let undefinedValue = undefined;

// 安全檢查
if (nullValue === null) {
    console.log("值是null");
}

if (undefinedValue === undefined) {
    console.log("值是undefined");
}

// 安全屬性存取
let obj = null;
// console.log(obj.property);  // TypeError
console.log(obj?.property);  // undefined (安全存取)

// 安全陣列存取
let arr = null;
// console.log(arr[0]);  // TypeError
console.log(arr?.[0]);  // undefined (安全存取)
```

```c !! c
#include <stdio.h>

int main() {
    // 空指標
    int* null_ptr = NULL;
    
    // 檢查空指標
    if (null_ptr == NULL) {
        printf("指標是空的\n");
    }
    
    // 安全的空指標檢查
    if (null_ptr != NULL) {
        printf("值: %d\n", *null_ptr);
    } else {
        printf("不能解參照空指標\n");
    }
    
    // 常見的空指標模式
    int* ptr1 = NULL;  // 初始化為空
    int* ptr2 = 0;     // 與NULL相同
    
    // 可能返回空的函數
    int* getPointer() {
        // 可能失敗的某些條件
        return NULL;
    }
    
    int* result = getPointer();
    if (result != NULL) {
        printf("獲得有效指標: %d\n", *result);
    } else {
        printf("獲得空指標\n");
    }
    
    return 0;
}
```
</UniversalEditor>

### 5.2 懸空指標

<UniversalEditor title="懸空指標" compare={true}>
```javascript !! js
// JavaScript - 垃圾回收防止懸空參照
function createObject() {
    let obj = { data: 42 };
    return obj;
}

let ref = createObject();
console.log(ref.data);  // 42

// JavaScript自動管理記憶體
// 沒有懸空參照的風險
// 垃圾回收處理清理
```

```c !! c
#include <stdio.h>
#include <stdlib.h>

// 返回區域變數指標的函數（危險！）
int* dangerousFunction() {
    int local_var = 42;
    return &local_var;  // 懸空指標！區域變數將被銷毀
}

// 返回分配記憶體的函數（安全）
int* safeFunction() {
    int* ptr = malloc(sizeof(int));
    if (ptr != NULL) {
        *ptr = 42;
    }
    return ptr;  // 安全 - 分配的記憶體持續存在
}

int main() {
    // 懸空指標範例
    int* dangler = dangerousFunction();
    // printf("懸空指標值: %d\n", *dangler);  // 未定義行為！
    
    // 安全指標範例
    int* safe_ptr = safeFunction();
    if (safe_ptr != NULL) {
        printf("安全指標值: %d\n", *safe_ptr);
        free(safe_ptr);  // 清理分配的記憶體
    }
    
    // 常見懸空指標場景
    int* ptr1 = malloc(sizeof(int));
    if (ptr1 != NULL) {
        *ptr1 = 100;
        free(ptr1);  // 記憶體已釋放
        // ptr1現在是懸空指標
        ptr1 = NULL;  // 好習慣：釋放後設定為NULL
    }
    
    return 0;
}
```
</UniversalEditor>

## 6. 指標和函數

### 6.1 向函數傳遞指標

<UniversalEditor title="指標和函數" compare={true}>
```javascript !! js
// JavaScript - 物件透過參照傳遞
function modifyObject(obj) {
    obj.value = 100;  // 修改原始物件
}

function modifyPrimitive(value) {
    value = 100;  // 不修改原始值（按值傳遞）
}

let obj = { value: 42 };
let primitive = 42;

console.log("之前:", obj.value, primitive);

modifyObject(obj);
modifyPrimitive(primitive);

console.log("之後:", obj.value, primitive);  // 100, 42
```

```c !! c
#include <stdio.h>

// 接受指標參數的函數
void modifyValue(int* ptr) {
    if (ptr != NULL) {
        *ptr = 100;  // 修改原始值
    }
}

// 接受值參數的函數
void modifyValueByValue(int value) {
    value = 100;  // 只修改區域副本
}

// 返回指標的函數
int* createValue() {
    static int value = 42;  // 靜態以在函數返回後持續存在
    return &value;
}

int main() {
    int x = 42;
    
    printf("修改前: x = %d\n", x);
    
    // 傳遞x的位址
    modifyValue(&x);
    printf("modifyValue後: x = %d\n", x);
    
    // 傳遞x的值
    modifyValueByValue(x);
    printf("modifyValueByValue後: x = %d\n", x);
    
    // 從函數獲取指標
    int* ptr = createValue();
    printf("來自函數的值: %d\n", *ptr);
    
    return 0;
}
```
</UniversalEditor>

### 6.2 函數指標

<UniversalEditor title="函數指標" compare={true}>
```javascript !! js
// JavaScript - 函數是一等物件
function add(a, b) {
    return a + b;
}

function multiply(a, b) {
    return a * b;
}

function subtract(a, b) {
    return a - b;
}

// 函數參照
let operation = add;
console.log(operation(5, 3));  // 8

operation = multiply;
console.log(operation(5, 3));  // 15

operation = subtract;
console.log(operation(5, 3));  // 2

// 函數陣列
let operations = [add, multiply, subtract];
for (let op of operations) {
    console.log(op(10, 2));
}
```

```c !! c
#include <stdio.h>

// 函數宣告
int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

int subtract(int a, int b) {
    return a - b;
}

int main() {
    // 函數指標宣告
    int (*operation)(int, int);
    
    // 將函數賦值給指標
    operation = add;
    printf("add(5, 3) = %d\n", operation(5, 3));
    
    operation = multiply;
    printf("multiply(5, 3) = %d\n", operation(5, 3));
    
    operation = subtract;
    printf("subtract(5, 3) = %d\n", operation(5, 3));
    
    // 函數指標陣列
    int (*operations[])(int, int) = {add, multiply, subtract};
    char* names[] = {"add", "multiply", "subtract"};
    
    for (int i = 0; i < 3; i++) {
        printf("%s(10, 2) = %d\n", names[i], operations[i](10, 2));
    }
    
    return 0;
}
```
</UniversalEditor>

## 7. 指標和陣列

### 7.1 陣列-指標關係

<UniversalEditor title="陣列和指標" compare={true}>
```javascript !! js
// JavaScript - 陣列是物件
let arr = [1, 2, 3, 4, 5];

// 陣列存取
console.log(arr[0]);  // 1
console.log(arr[1]);  // 2

// 陣列方法
arr.push(6);
arr.pop();

// 陣列長度
console.log("長度:", arr.length);

// 陣列迭代
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}
```

```c !! c
#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int* ptr = arr;  // 陣列名衰減為指向第一個元素的指標
    
    printf("陣列元素:\n");
    
    // 陣列表示法
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    printf("\n指標表示法:\n");
    
    // 指標表示法
    for (int i = 0; i < 5; i++) {
        printf("*(ptr + %d) = %d\n", i, *(ptr + i));
    }
    
    printf("\n混合表示法:\n");
    
    // 混合表示法
    for (int i = 0; i < 5; i++) {
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }
    
    // 陣列大小 vs 指標大小
    printf("\n大小:\n");
    printf("陣列大小: %zu 位元組\n", sizeof(arr));
    printf("指標大小: %zu 位元組\n", sizeof(ptr));
    printf("元素數量: %zu\n", sizeof(arr) / sizeof(arr[0]));
    
    return 0;
}
```
</UniversalEditor>

### 7.2 多維陣列和指標

<UniversalEditor title="多維陣列" compare={true}>
```javascript !! js
// JavaScript - 巢狀陣列
let matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

// 存取元素
console.log(matrix[0][0]);  // 1
console.log(matrix[1][1]);  // 5
console.log(matrix[2][2]);  // 9

// 遍歷矩陣
for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
        console.log(`matrix[${i}][${j}] = ${matrix[i][j]}`);
    }
}
```

```c !! c
#include <stdio.h>

int main() {
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    
    // 指向第一個元素的指標
    int* ptr = &matrix[0][0];
    
    printf("使用指標的矩陣元素:\n");
    
    // 使用指標算術存取
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("matrix[%d][%d] = %d (位址: %p)\n", 
                   i, j, *(ptr + i * 3 + j), ptr + i * 3 + j);
        }
    }
    
    // 陣列表示法
    printf("\n使用陣列表示法的矩陣元素:\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]);
        }
    }
    
    // 指向陣列的指標（行指標）
    int (*row_ptr)[3] = matrix;
    
    printf("\n使用行指標的矩陣元素:\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("matrix[%d][%d] = %d\n", i, j, row_ptr[i][j]);
        }
    }
    
    return 0;
}
```
</UniversalEditor>

## 8. 指標安全最佳實踐

### 8.1 安全指標程式設計

<UniversalEditor title="安全指標實踐" compare={true}>
```javascript !! js
// JavaScript - 內建安全特性
function safeOperations() {
    let value = 42;
    let ref = value;
    
    // JavaScript提供:
    // - 自動邊界檢查
    // - Null/undefined保護
    // - 垃圾回收
    // - 類型安全
    
    console.log("安全操作:", value, ref);
}

safeOperations();
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void safePointerOperations() {
    // 1. 始終初始化指標
    int* ptr1 = NULL;  // 好：初始化為NULL
    int value = 42;
    int* ptr2 = &value;  // 好：用有效位址初始化
    
    // 2. 解參照前檢查空指標
    if (ptr1 != NULL) {
        printf("ptr1值: %d\n", *ptr1);
    } else {
        printf("ptr1是空的\n");
    }
    
    if (ptr2 != NULL) {
        printf("ptr2值: %d\n", *ptr2);
    }
    
    // 3. 當指標不應修改資料時使用const
    const int* const_ptr = &value;
    // *const_ptr = 100;  // 錯誤：不能透過const指標修改
    
    // 4. 陣列存取前檢查邊界
    int arr[] = {1, 2, 3, 4, 5};
    int* arr_ptr = arr;
    int index = 3;
    
    if (index >= 0 && index < 5) {
        printf("arr[%d] = %d\n", index, arr_ptr[index]);
    } else {
        printf("索引越界\n");
    }
    
    // 5. 釋放分配的記憶體並設定為NULL
    int* dynamic_ptr = malloc(sizeof(int));
    if (dynamic_ptr != NULL) {
        *dynamic_ptr = 100;
        printf("動態值: %d\n", *dynamic_ptr);
        free(dynamic_ptr);
        dynamic_ptr = NULL;  // 防止懸空指標
    }
}

int main() {
    safePointerOperations();
    return 0;
}
```
</UniversalEditor>

### 8.2 常見指標錯誤

<UniversalEditor title="常見指標錯誤" compare={true}>
```javascript !! js
// JavaScript - 受保護免受大多數類似指標的錯誤
function safeOperations() {
    let arr = [1, 2, 3];
    
    // JavaScript防止:
    // - 緩衝區溢位
    // - 懸空參照
    // - 記憶體洩漏
    // - 空指標解參照
    
    // 安全陣列存取
    console.log(arr[10]);  // undefined (安全)
    
    // 安全物件存取
    let obj = null;
    console.log(obj?.property);  // undefined (安全)
}
```

```c !! c
#include <stdio.h>
#include <stdlib.h>

void demonstrateCommonMistakes() {
    // 錯誤1：未初始化指標
    int* uninit_ptr;  // 包含垃圾位址
    // printf("%d", *uninit_ptr);  // 未定義行為！
    
    // 正確：初始化指標
    int* safe_ptr = NULL;
    if (safe_ptr != NULL) {
        printf("%d", *safe_ptr);
    }
    
    // 錯誤2：解參照空指標
    int* null_ptr = NULL;
    // printf("%d", *null_ptr);  // 段錯誤！
    
    // 正確：檢查空指標
    if (null_ptr != NULL) {
        printf("%d", *null_ptr);
    }
    
    // 錯誤3：緩衝區溢位
    int arr[5] = {1, 2, 3, 4, 5};
    int* arr_ptr = arr;
    // printf("%d", arr_ptr[10]);  // 緩衝區溢位！
    
    // 正確：檢查邊界
    int index = 10;
    if (index >= 0 && index < 5) {
        printf("%d", arr_ptr[index]);
    }
    
    // 錯誤4：記憶體洩漏
    int* leak_ptr = malloc(sizeof(int));
    // 使用記憶體...
    // 忘記free(leak_ptr);  // 記憶體洩漏！
    
    // 正確：始終釋放分配的記憶體
    int* correct_ptr = malloc(sizeof(int));
    if (correct_ptr != NULL) {
        *correct_ptr = 42;
        free(correct_ptr);
        correct_ptr = NULL;
    }
    
    // 錯誤5：懸空指標
    int* dangler = malloc(sizeof(int));
    free(dangler);
    // printf("%d", *dangler);  // 懸空指標！
    
    // 正確：釋放後設定為NULL
    int* safe_dangler = malloc(sizeof(int));
    free(safe_dangler);
    safe_dangler = NULL;  // 現在可以安全檢查
}

int main() {
    demonstrateCommonMistakes();
    return 0;
}
```
</UniversalEditor>

## 9. 除錯指標

### 9.1 指標除錯技術

<UniversalEditor title="指標除錯" compare={true}>
```javascript !! js
// JavaScript - 除錯工具
function debugExample() {
    let value = 42;
    let obj = { data: value };
    
    // JavaScript除錯
    console.log("值:", value);
    console.log("物件:", obj);
    console.log("值的類型:", typeof value);
    console.log("物件的類型:", typeof obj);
    
    // 瀏覽器開發工具提供:
    // - 變數檢查
    // - 呼叫堆疊
    // - 記憶體分析
    // - 參照追蹤
}
```

```c !! c
#include <stdio.h>
#include <stdlib.h>

void debugPointers() {
    int value = 42;
    int* ptr = &value;
    int* null_ptr = NULL;
    int* uninit_ptr;
    
    // 列印指標資訊
    printf("=== 指標除錯 ===\n");
    printf("值: %d\n", value);
    printf("值位址: %p\n", &value);
    printf("指標值: %p\n", ptr);
    printf("解參照的指標: %d\n", *ptr);
    
    // 檢查空指標
    printf("\n空指標檢查:\n");
    if (null_ptr == NULL) {
        printf("null_ptr是NULL\n");
    } else {
        printf("null_ptr不是NULL: %p\n", null_ptr);
    }
    
    // 檢查未初始化指標
    printf("\n未初始化指標:\n");
    printf("uninit_ptr包含: %p\n", uninit_ptr);
    
    // 陣列除錯
    int arr[] = {1, 2, 3, 4, 5};
    int* arr_ptr = arr;
    
    printf("\n陣列除錯:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d (位址: %p)\n", i, arr_ptr[i], &arr_ptr[i]);
    }
    
    // 記憶體分配除錯
    int* dynamic_ptr = malloc(sizeof(int));
    if (dynamic_ptr != NULL) {
        *dynamic_ptr = 100;
        printf("\n動態分配:\n");
        printf("分配位址: %p\n", dynamic_ptr);
        printf("分配值: %d\n", *dynamic_ptr);
        free(dynamic_ptr);
        printf("記憶體已釋放\n");
    } else {
        printf("記憶體分配失敗\n");
    }
}

int main() {
    debugPointers();
    return 0;
}
```
</UniversalEditor>

## 10. 下一步

在掌握指標基礎後，你將：

1. **理解記憶體位址**: 了解指標如何與實體記憶體相關
2. **掌握指標算術**: 高效導航記憶體
3. **編寫安全指標程式碼**: 避免常見指標陷阱
4. **在函數中使用指標**: 高效傳遞資料
5. **除錯指標問題**: 識別和修復指標相關錯誤

**下一模組**: 陣列和字串 - 我們將探索指標如何與陣列和C語言中的字串操作一起工作。

準備好深入C語言的記憶體模型了嗎？讓我們繼續學習陣列和字串！ 